package com.jwater.core.manager.master.choose;

import com.jwater.core.common.param.ParamMap;
import com.jwater.core.manager.common.ManagerException;
import com.jwater.core.manager.common.conf.CoreConfParse;
import com.jwater.core.manager.common.model.JobInfo;
import com.jwater.core.manager.common.model.NodeInfo;
import com.jwater.core.manager.common.model.WorkerInfo;
import com.jwater.core.manager.master.MasterNodeImpl;
import com.jwater.core.manager.proxy.ProxyClient;
import com.jwater.core.manager.proxy.ProxyManager;
import jwater.org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @description:按照随机策略选择主机
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public class RandomChooseHost implements ChooseHost {
    private MasterNodeImpl masterNode;

    public RandomChooseHost(MasterNodeImpl masterNode) {
        this.masterNode = masterNode;
    }

    /**
     * 工作进程选择主机
     *
     * @param oldHost
     * @param workerInfo
     * @param index
     * @return
     */
    @Override
    public NodeInfo choose(String oldHost, WorkerInfo workerInfo, int index) {
        ExpectGroupNode expect = getExpectGroupNode(workerInfo.getProps(), index);
        boolean autoNode = "true".equals(workerInfo.getProps().getString("autoNode", "false"));
        return choose(oldHost, expect, autoNode);
    }

    /**
     * 离线任务选择主机
     *
     * @param oldHost
     * @param jobInfo
     * @return
     */
    @Override
    public NodeInfo choose(String oldHost, JobInfo jobInfo) {
        ExpectGroupNode expect = null;
        String node = jobInfo.getProps().getString("node");
        if (!StringUtils.isEmpty(node)) {
            expect = new ExpectGroupNode(node);
        }
        boolean autoNode = "true".equals(jobInfo.getProps().getString("autoNode", "false"));
        return choose(oldHost, expect, autoNode);
    }

    /**
     * 选择主机
     *
     * @param oldHost
     * @param expect
     * @param autoNode
     * @return
     */
    private NodeInfo choose(String oldHost, ExpectGroupNode expect, boolean autoNode) {
        // 固定主机运行
        if (expect != null && !StringUtils.isEmpty(expect.groupName) && !StringUtils.isEmpty(expect.nodeName)) {
            NodeInfo nodeInfo = masterNode.coreConf.getNodeInfo(expect.groupName, expect.nodeName);
            if (nodeInfo == null) {
                throw new ManagerException("can not choose not exist host[" + nodeInfo.getHost() + "]");
            }
            ProxyClient proxyClient = masterNode.proxyManager.getProxyClient(nodeInfo.getHost());
            if (proxyClient == null || !proxyClient.isConnect()) {
                throw new ManagerException("can not choose not online host[" + nodeInfo.getHost() + "]");
            }
            return nodeInfo;
        }
        // 非固定主机运行
        List<NodeInfo> nodes = masterNode.coreConf.getNodeList();
        if (expect != null && !StringUtils.isEmpty(expect.groupName)) {
            nodes = masterNode.coreConf.getNodeList(expect.groupName);
        }
        // 进程第1次运行，随机选择一台主机
        if (StringUtils.isEmpty(oldHost)) {
            return randomNode(nodes);
        }
        // 已经存在之前的主机信息，优先选择之前的主机
        NodeInfo nodeInfo = masterNode.coreConf.getNodeInfo(oldHost);
        ProxyClient proxyClient = masterNode.proxyManager.getProxyClient(nodeInfo.getHost());
        if (proxyClient != null && proxyClient.isConnect()) {
            return nodeInfo;
        }
        // 之前的主机不在线，判断是否能切换主机
        if (autoNode) {
            // 能切换的，随机选择一台主机
            return randomNode(nodes);
        } else {
            // 不能切换抛出异常
            throw new ManagerException("can not choose old not online host[" + oldHost + "]");
        }
    }

    /**
     * 随机选择一台主机
     *
     * @param nodes
     * @return
     */
    private NodeInfo randomNode(List<NodeInfo> nodes) {
        nodes = filterOnline(nodes);
        if (nodes.isEmpty()) {
            throw new ManagerException("can not choose host,all not online");
        }
        if (nodes.size() == 1) {
            return nodes.get(0);
        }
        return nodes.get(new Random(System.currentTimeMillis()).nextInt(nodes.size()));
    }

    /**
     * 过滤在线的主机
     *
     * @param nodes
     * @return
     */
    private List<NodeInfo> filterOnline(List<NodeInfo> nodes) {
        List<NodeInfo> list = new ArrayList<NodeInfo>();
        for (NodeInfo node : nodes) {
            ProxyClient proxyClient = masterNode.proxyManager.getProxyClient(node.getHost());
            if (proxyClient != null && proxyClient.isConnect()) {
                list.add(node);
            }
        }
        return list;
    }

    /**
     * 对以下格式进行解析
     * <prop key="node.(1,2,3,4-6)" value="default:node1" />
     * <prop key="node.(5-10,13,45)" value="default:node2" />
     * <prop key="node" value="default:node3" />
     *
     * @param props
     * @param index
     * @return
     */
    private ExpectGroupNode getExpectGroupNode(ParamMap props, int index) {
        for (Map.Entry<String, Object> en : props.getByPrefix("node", true).entrySet()) {
            for (String indexs : en.getKey().replace("(", "")
                    .replace(")", "").split(",")) {
                String[] items = indexs.split("-");
                if (items.length == 1) {
                    if (index == Integer.parseInt(items[0])) {
                        return new ExpectGroupNode(en.getValue().toString());
                    }
                } else if (items.length == 2) {
                    int start = Integer.parseInt(items[0]);
                    int end = Integer.parseInt(items[1]);
                    if (index >= start && index <= end) {
                        return new ExpectGroupNode(en.getValue().toString());
                    }
                }
            }
        }
        String node = props.getString("node");
        if (!StringUtils.isEmpty(node)) {
            return new ExpectGroupNode(node);
        }
        return null;
    }

    protected class ExpectGroupNode {
        public String groupName;
        public String nodeName;

        public ExpectGroupNode(String node) {
            if (!StringUtils.isEmpty(node)) {
                String[] items = node.split(":");
                if (items.length == 1) {
                    groupName = items[0];
                } else if (items.length >= 2) {
                    groupName = items[0];
                    nodeName = items[1];
                }
            }
        }
    }
}
